﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;

namespace Hive.Core
{
    public static class Log
    {
        static string LogDir = @".\logs\";
        public static void Write(int depth, string message, params object[] args)
        {
            Write(depth, string.Format(message, args));
        }
        public static void Write(string message, params object[] args)
        {
            Write(string.Format(message, args));
        }
        public static void Write(string message)
        {
            Write(0, message);
        }
        public static void Write(int depth, string message)
        {
            var pad = new string('\t', depth);

            WriteToFile("log.txt", pad + message + Environment.NewLine);
        }

        private static void WriteToFile(string file, string message)
        {
//#if DEBUG
            var folder = Path.Combine(LogDir, Assembly.GetEntryAssembly().GetName().Name);
            if (!Directory.Exists(folder))
                Directory.CreateDirectory(folder);
            File.AppendAllText(Path.Combine(folder, file), message);

            //File.AppendAllText(Path.Combine(LogDir, file), message);
//#endif
        }

        public static void WriteMap()
        {
            int turn = Env.CurrState.TurnNumber;
            GameState state = Env.CurrState;
            StringBuilder map = new StringBuilder();
            map.AppendLine("========== " + turn.ToString() + "===========");
            for (int r = 0; r < Env.Map.Height; r++)
            {
                for (int c = 0; c < Env.Map.Width; c++)
                {
                    var chr = ' ';
                    switch (Env.Map[r, c])
                    {
                        case Tile.Land: chr = ' '; break;
                        case Tile.Water: chr = '#'; break;
                        case Tile.Unseen: chr = '.'; break;
                    }

                    var ant = state.MyAnts.Find(a => a.Row == r && a.Col == c);
                    if (ant != null)
                        chr = 'A';
                    var food = state.FoodTiles.Find(f => f.Row == r && f.Col == c);
                    if (food != null)
                        chr = 'F';

                    map.Append(chr);
                }

                map.AppendLine();
            }

            WriteToFile("map.txt", map.ToString());
        }

        public static void WriteVisited(bool[,] visited, Location start)
        {
            StringBuilder map = new StringBuilder();
            map.AppendLine(string.Format("====Visited====== at {0}", start.ToString()));
            for (int r = 0; r < Env.Map.Height; r++)
            {
                for (int c = 0; c < Env.Map.Width; c++)
                {

                    if (visited[r, c])
                    {
                        map.Append(' ');
                    }
                    else
                    {
                        map.Append('*');
                    }
                }

                map.AppendLine();
            }

            //File.AppendAllText(Path.Combine(LogDir, "map.txt"), map.ToString());
        }

        public static void WriteEfficiencies(int[,] efficiencyMatrix)
        {
            WriteEfficiencies(efficiencyMatrix, (i, j) => true);
        }

        public static void WriteEfficiencies(int[,] efficiencyMatrix, Func<int, int, bool> predicate)
        {
            const int pad = 5;
            var sb = new StringBuilder();
            sb.AppendLine("Efficiency matrix");
            sb.Append(" ".PadLeft(pad));
            for (var j = 0; j < efficiencyMatrix.GetLength(1); j++)
                sb.AppendFormat("{0}".PadLeft(pad), Env.MyAnts[j].Id);
            sb.AppendLine();
            sb.AppendLine("-------------");
            for (var i = 0; i < efficiencyMatrix.Height(); i++)
            {
                sb.AppendFormat("{0}".PadRight(pad), i);
                for (var j = 0; j < efficiencyMatrix.Width(); j++)
                {
                    if (predicate(i, j))
                    {
                        sb.AppendFormat("{0}".PadLeft(pad), efficiencyMatrix[i, j]);
                    }
                    else
                    {
                        sb.Append("--".PadLeft(pad));
                    }
                }
                sb.AppendLine();
            }
            sb.AppendLine("-------------");
            Log.Write(sb.ToString());
        }

        public static void WritePerformance()
        {
            StringBuilder map = new StringBuilder();
            
            map.AppendLine(string.Format("Turn number: {0}", Env.CurrState.TurnNumber));
            
            foreach (var s in Perf.sections.Keys)
            {
                map.AppendLine(string.Format("section {0} took {1} ms call count {2}, average {3}", s, Perf.sections[s].Total, Perf.sections[s].Times, Perf.sections[s].Average));
            }


            Perf.sections = new Dictionary<string, Counter>();

            //File.AppendAllText(Path.Combine(LogDir, "perf.txt"), map.ToString());
            WriteToFile("perf.txt", map.ToString());
        }


        public static void WriteEfficiencies(int[,] efficiencyMatrix, bool[] processedAnts, bool[] processedTasks)
        {
            WriteEfficiencies(efficiencyMatrix, (i, j) => !processedTasks[i] && !processedAnts[j]);
        }


        public static void WriteStatistics()
        {
            Log.WriteMap();

            Log.Write("Current Stats:");
            Log.Write("===================================");
            Log.Write(1, "Turn number: {0}", Env.CurrState.TurnNumber);
            Log.Write(1, String.Format("Unseen tiles: {0}", GetUnseenTilesCount()));
            if (GetUnseenTilesCount() < 10)
            {
                Env.Map.Iter((r, c, t) => { if (t == Tile.Unseen) Log.Write("{0} {1}", r, c); });
            }
            //Log.Write(1, String.Format("Food tiles: {0} ({1} previously)", currentState.FoodTiles.Count, prevState == null ? 0 : prevState.FoodTiles.Count));
            Log.Write("===================================");

            var ga = Env.MyAnts.GroupBy(a => a.Location).Where(i => i.Count() > 1);
            foreach (var g in ga)
            {
                Log.Write("At {0} there are {1} ants", g.Key, g.Count());
            }

            foreach (var a in Env.MyAnts)
            {
                Log.Write("Ant at {0}[{1}] corresponding ant {2}", a.Location, a.Id, Env.CurrState.MyAnts.Any(ant => ant == a.Location) ? "exists" : "DO NOT exists");
            }
        }

       

        private static int GetUnseenTilesCount()
        {
            var result = 0;
            Env.Map.Iter((r, c, t) => result += t == Tile.Unseen ? 1 : 0);

            return result;
        }

        public static void LogPathCalculation(int[,] calculation)
        {
            StringBuilder map = new StringBuilder();
            map.AppendLine("PathCalculation");
            for (int r = 0; r < calculation.Height(); r++)
            {
                for (int c = 0; c < calculation.Width(); c++)
                {
                    var str = calculation[r,c].ToString();

                    map.Append(str.PadLeft(3));
                }

                map.AppendLine();
            }

            WriteToFile("map.txt", map.ToString());
        }
    }
}
